﻿#region File Description
//-----------------------------------------------------------------------------
// TaskFactory
//
// Copyright © 2013 Wave Coorporation. All rights reserved.
// Use is subject to license terms.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using System;
using System.Collections.Generic;
using WaveEngine.Framework;
using WaveEngine.Framework.Animation;
using System.Linq;
using WaveEngine.Common.Media;
using WaveEngine.Components.Gestures;
using WaveEngine.Extensions.ITasks.PrimiviteTasks;
#endregion

namespace WaveEngine.Extensions.ITasks
{
    public static class TaskFactory
    {
        /// <summary>
        /// Continues the with.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="nextTaskGenerator">The next task generator.</param>
        /// <returns></returns>
        public static ITask ContinueWith(this ITask parent, Func<ITask> nextTaskGenerator)
        {
            return TaskFactory.ContinueWith(parent, null as string, nextTaskGenerator);
        }

        /// <summary>
        /// Continues the with.
        /// </summary>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="parent">The parent.</param>
        /// <param name="nextTaskGenerator">The next task generator.</param>
        /// <returns></returns>
        public static ITask ContinueWith<TResult>(this ITask<TResult> parent, Func<TResult, ITask> nextTaskGenerator)
        {
            return new DeferedContinueWithTask<TResult>(parent, null as string, nextTaskGenerator);
        }

        /// <summary>
        /// Continues the with.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="taskName">Name of the task.</param>
        /// <param name="nextTaskGenerator">The next task generator.</param>
        /// <returns></returns>
        public static ITask ContinueWith(this ITask parent, string taskName, Func<ITask> nextTaskGenerator)
        {
            return new DeferedTaskNode(parent, taskName, nextTaskGenerator);
        }

        /// <summary>
        /// Continues the with.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="nextTask">The next task.</param>
        /// <returns></returns>
        public static ITask ContinueWith(this ITask parent, ITask nextTask)
        {
            return ContinueWith(parent, null as string, nextTask);
        }

        /// <summary>
        /// Continues the with.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="taskName">Name of the task.</param>
        /// <param name="nextTask">The next task.</param>
        /// <returns></returns>
        /// <exception cref="System.NotSupportedException">It is not possible to continue with, aborted or finised task. Defer the run command to a posterior stage.</exception>
        public static ITask ContinueWith(this ITask parent, string taskName, ITask nextTask)
        {
            if (nextTask.TaskState == WaveEngine.Framework.Services.TaskState.Finished
                || nextTask.TaskState == WaveEngine.Framework.Services.TaskState.Aborted)
                throw new NotSupportedException("It is not possible to continue with, aborted or finised task. Defer the run command to a posterior stage.");

            return new TaskNode(parent, taskName, nextTask);
        }

        /// <summary>
        /// Continues the with.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="nextAction">The next action.</param>
        /// <returns></returns>
        public static ITask ContinueWith(this ITask parent, Action nextAction)
        {
            return new TaskNode(parent, null, new ActionTask(parent.Scene, null, nextAction));
        }

        /// <summary>
        /// Continues with if condition. Condition is evalued in time.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="condition">if set to <c>true</c> [condition] runs the task.</param>
        /// <param name="task">The task.</param>
        /// <returns>Empty task if [condition] false, task if true.</returns>
        public static ITask ContinueIfCondition(this ITask parent, Func<bool> condition, ITask task)
        {
            ITask checkTask = new DeferedTaskNode(parent.Scene, () =>
            {
                if (condition())
                {
                    return task;
                }
                else
                {
                    return parent.Scene.CreateEmptyTask();
                }
            });

            return ContinueWith(parent, null as string, checkTask);
        }

        /// <summary>
        /// Continues with if condition.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="condition">The condition.</param>
        /// <param name="taskGenerator">The task generator.</param>
        /// <returns></returns>
        public static ITask ContinueIfCondition(this ITask parent, Func<bool> condition, Func<ITask> taskGenerator)
        {
            return ContinueIfCondition(parent, condition, TaskFactory.CreateTask(parent.Scene, taskGenerator));
        }

        /// <summary>
        /// Continues the with.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="taskName">Name of the task.</param>
        /// <param name="nextAction">The next action.</param>
        /// <returns></returns>
        public static ITask ContinueWith(this ITask parent, string taskName, Action nextAction)
        {
            return new TaskNode(parent, taskName, new ActionTask(parent.Scene, "Internal " + taskName, nextAction));
        }

        /// <summary>
        /// Ands the wait condition actively.
        /// </summary>
        /// <returns></returns>
        public static ITask AndWaitConditionActively(this ITask parent, Func<bool> Predicate)
        {
            return new TaskNode(parent, null, new ActiveWaitConditionTask(parent.Scene, Predicate));
        }

        /// <summary>
        /// Continues the with single animation.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="singleAnimation">The single animation.</param>
        /// <param name="animationUI">The animation UI.</param>
        /// <param name="dependencyProperty">The dependency property.</param>
        /// <returns></returns>
        public static ITask ContinueWithSingleAnimation(this ITask parent, SingleAnimation singleAnimation, AnimationUI animationUI, DependencyProperty dependencyProperty)
        {
            return parent.ContinueWith(() => parent.Scene.CreateSingleAnimationTask(singleAnimation, animationUI, dependencyProperty));
        }

        /// <summary>
        /// Ands the parallely.
        /// </summary>
        /// <param name="brother">The brother.</param>
        /// <param name="parallelTask">The parallel task.</param>
        /// <returns></returns>
        public static ITaskSet AndParallely(this ITask brother, ITask parallelTask)
        {
            TaskBase botherTask = brother as TaskBase;
            if (botherTask == null)
            {
                return new TaskSetOperationSelector(brother.Scene, null, new ITask[] { brother, parallelTask });
            }
            else if (botherTask.Parent != null)
            {
                return new TaskSetOperationSelector(botherTask.Parent, null, new ITask[] { botherTask, parallelTask });

            }
            else
            {
                return new TaskSetOperationSelector(botherTask.Scene, null, new ITask[] { botherTask, parallelTask });
            }
        }

        /// <summary>
        /// Ands the wait interval.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="time">The time.</param>
        /// <returns></returns>
        public static ITask Delay(this ITask parent, TimeSpan time)
        {
            return new WaitTimeTask(parent, time);
        }

        /// <summary>
        /// Ands the play video.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="vinfo">The vinfo.</param>
        /// <returns></returns>
        public static ITask AndPlayVideo(this ITask parent, VideoInfo vinfo)
        {
            return new VideoTask(parent, null, vinfo);
        }

        /// <summary>
        /// Ands the wait tap.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="touchGestures">The touch gestures.</param>
        /// <returns></returns>
        public static ITask AndWaitTap(this ITask parent, TouchGestures touchGestures)
        {
            return new TapTask(parent, null, touchGestures);
        }

        /// <summary>
        /// Continues the and wait sub tasks defered.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="nextTaskGenerators">The next task generators.</param>
        /// <returns></returns>
        public static ITaskSet ContinueWith(this ITask parent, params Func<ITask>[] nextTaskGenerators)
        {
            return new TaskSetOperationSelector(parent, null, nextTaskGenerators);
        }

        /// <summary>
        /// Continues the and wait sub tasks defered.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="childTasks">The child tasks.</param>
        /// <returns></returns>
        public static ITaskSet ContinueWith(this ITask parent, IEnumerable<ITask> childTasks)
        {
            return new TaskSetOperationSelector(parent, null, childTasks);
        }

        /// <summary>
        /// Continues the and wait sub tasks.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="nextTaskGenerators">The next task generators.</param>
        /// <returns></returns>
        public static ITaskSet ContinueWith(this ITask parent, params ITask[] childTasks)
        {
            return new TaskSetOperationSelector(parent, null, childTasks);
        }

        /// <summary>
        /// Continues the with.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="name">The name.</param>
        /// <param name="childTasks">The child tasks.</param>
        /// <returns></returns>
        public static ITaskSet ContinueWith(this ITask parent, string name, params ITask[] childTasks)
        {
            return new TaskSetOperationSelector(parent, name, childTasks);
        }


        //--------------------------------------------------------------------------------
        /// <summary>
        /// Creates the empty task.
        /// </summary>
        /// <param name="scene">The scene.</param>
        /// <returns></returns>
        public static ITask CreateEmptyTask(this Scene scene)
        {
            return new ActionTask(scene, null, () => { });
        }

        /// <summary>
        /// Creates the empty task.
        /// </summary>
        /// <param name="scene">The scene.</param>
        /// <param name="taskName">Name of the task.</param>
        /// <returns></returns>
        public static ITask CreateEmptyTask(this Scene scene, string taskName)
        {
            return new ActionTask(scene, taskName, () => { });
        }

        /// <summary>
        /// Creates the wait task.
        /// </summary>
        /// <param name="scene">The scene.</param>
        /// <param name="timeSpan">The time span.</param>
        /// <returns></returns>
        public static ITask CreateWaitTask(this BaseScene scene, TimeSpan timeSpan)
        {
            return new WaitTimeTask(scene, timeSpan);
        }

        /// <summary>
        /// Creates the action task.
        /// </summary>
        /// <param name="scene">The scene.</param>
        /// <param name="action">The action.</param>
        /// <returns></returns>
        public static ITask CreateTask(this Scene scene, Func<ITask> action)
        {
            return new DeferedTaskNode(scene, action);
        }

        /// <summary>
        /// Creates the task.
        /// </summary>
        /// <param name="scene">The scene.</param>
        /// <param name="internalTask">The internal task.</param>
        /// <returns></returns>
        public static ITask CreateTask(this Scene scene, ITask internalTask)
        {
            return CreateTask(scene, null, internalTask);
        }

        /// <summary>
        /// Creates the task.
        /// </summary>
        /// <param name="scene">The scene.</param>
        /// <param name="name">The name.</param>
        /// <param name="internalTask">The internal task.</param>
        /// <returns></returns>
        public static ITask CreateTask(this Scene scene, string name, ITask internalTask)
        {
            return new TaskNode(scene, name, internalTask);
        }

        /// <summary>
        /// Creates the action task.
        /// </summary>
        /// <param name="scene">The scene.</param>
        /// <param name="action">The action.</param>
        /// <returns></returns>
        public static ITask CreateActionTask(this Scene scene, Action action)
        {
            ITask actiontask = new ActionTask(scene, null, action);
            return actiontask;
        }

        /// <summary>
        /// Creates the token task.
        /// </summary>
        /// <param name="scene">The scene.</param>
        /// <returns></returns>
        public static BasicTaskToken CreateTokenTask(this Scene scene, string name = null)
        {
            return new BasicTaskToken(scene, name);
        }

        /// <summary>
        /// Ands the wait condition.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="BreakPredicate">The predicate.</param>
        /// <returns></returns>
        public static ITask CreateWaitConditionTask(this Scene scene, Func<bool> BreakPredicate)
        {
            return new ActiveWaitConditionTask(scene, BreakPredicate);
        }

        /// <summary>
        /// Creates the single animation task.
        /// </summary>
        /// <param name="scene">The scene.</param>
        /// <param name="singleAnimation">The single animation.</param>
        /// <param name="animationUI">The animation UI.</param>
        /// <param name="dependencyProperty">The dependency property.</param>
        /// <returns></returns>
        public static ITask CreateSingleAnimationTask(this Scene scene, SingleAnimation singleAnimation, AnimationUI animationUI, DependencyProperty dependencyProperty)
        {
            return new SingleAnimationTask(scene, null, singleAnimation, animationUI, dependencyProperty);
        }

        /// <summary>
        /// Creates the video task.
        /// </summary>
        /// <param name="scene">The scene.</param>
        /// <param name="videoInfo">The video info.</param>
        /// <returns></returns>
        public static ITask CreateVideoTask(this Scene scene, VideoInfo videoInfo)
        {
            return new VideoTask(scene, null, videoInfo);
        }

        /// <summary>
        /// Creates the wait tap task.
        /// </summary>
        /// <param name="scene">The scene.</param>
        /// <param name="TouchGestures">The touch gestures.</param>
        /// <returns></returns>
        public static ITask CreateWaitTapTask(this Scene scene, TouchGestures touchGestures)
        {
            return new TapTask(scene, null, touchGestures);
        }
        /// <summary>
        /// Create parallel tasks.
        /// </summary>
        /// <param name="scene">The scene.</param>
        /// <param name="taskGenerators">The task generators.</param>
        /// <returns></returns>
        public static ITaskSet CreateParallelTasks(this Scene scene, string name, params Func<ITask>[] taskGenerators)
        {
            return new TaskSetOperationSelector(scene, name, taskGenerators);
        }

        /// <summary>
        /// Creates the repeat task until.
        /// </summary>
        /// <param name="scene">The scene.</param>
        /// <param name="taskGenerator">The task generator.</param>
        /// <param name="stopCondition">The until predicate.</param>
        /// <returns></returns>
        public static ITask CreateLoopTaskUntil(this Scene scene, Func<ITask> taskGenerator, Func<bool> stopCondition)
        {
            return scene.CreateTask(() =>
                {
                    if (stopCondition())
                        return scene.CreateEmptyTask();
                    else
                        return taskGenerator().ContinueWith(scene.CreateLoopTaskUntil(taskGenerator, stopCondition));
                });
        }

        /// <summary>
        /// Creates the parallel tasks.
        /// </summary>
        /// <param name="scene">The scene.</param>
        /// <param name="name">The name.</param>
        /// <param name="taskGenerators">The task generators.</param>
        /// <returns></returns>
        public static ITaskSet CreateParallelTasks(this Scene scene, string name, IEnumerable<Func<ITask>> taskGenerators)
        {
            return new TaskSetOperationSelector(scene, name, taskGenerators.ToArray());
        }

        /// <summary>
        /// Creates the parallel tasks.
        /// </summary>
        /// <param name="scene">The scene.</param>
        /// <param name="name">The name.</param>
        /// <param name="taskGenerators">The task generators.</param>
        /// <returns></returns>
        public static ITaskSet CreateParallelTasks(this Scene scene, IEnumerable<Func<ITask>> taskGenerators)
        {
            return new TaskSetOperationSelector(scene, null, taskGenerators.ToArray());
        }

        /// <summary>
        /// Creates the parallel tasks.
        /// </summary>
        /// <param name="scene">The scene.</param>
        /// <param name="name">The name.</param>
        /// <param name="childTasks">The child tasks.</param>
        /// <returns></returns>
        public static ITaskSet CreateParallelTasks(this Scene scene, string name, IEnumerable<ITask> childTasks)
        {
            return new TaskSetOperationSelector(scene, name, childTasks);
        }

        /// <summary>
        /// Create parallel tasks.
        /// </summary>
        /// <param name="scene">The scene.</param>
        /// <param name="taskGenerators">The task generators.</param>
        /// <returns></returns>
        public static ITaskSet CreateParallelTasks(this Scene scene, string name, params ITask[] childTasks)
        {
            return new TaskSetOperationSelector(scene, name, childTasks);
        }


        /// <summary>
        /// Creates the parallel tasks.
        /// </summary>
        /// <param name="scene">The scene.</param>
        /// <param name="name">The name.</param>
        /// <param name="taskGenerators">The task generators.</param>
        /// <returns></returns>
        public static ITaskSet CreateParallelTasks(this Scene scene, params Func<ITask>[] taskGenerators)
        {
            return scene.CreateParallelTasks(null as string, taskGenerators);
        }


        /// <summary>
        /// Creates the parallel tasks.
        /// </summary>
        /// <param name="scene">The scene.</param>
        /// <param name="name">The name.</param>
        /// <param name="childTasks">The child tasks.</param>
        /// <returns></returns>
        public static ITaskSet CreateParallelTasks(this Scene scene, params ITask[] childTasks)
        {
            return scene.CreateParallelTasks(null as string, childTasks);
        }

        /// <summary>
        /// Creates the parallel tasks.
        /// </summary>
        /// <param name="scene">The scene.</param>
        /// <param name="name">The name.</param>
        /// <param name="childTasks">The child tasks.</param>
        /// <returns></returns>
        public static ITaskSet CreateParallelTasks(this Scene scene, IEnumerable<ITask> childTasks)
        {
            return scene.CreateParallelTasks(null as string, childTasks);
        }


        /// <summary>
        /// Ases the skipable task.
        /// </summary>
        /// <param name="taskToSkip">The task to skip.</param>
        /// <returns></returns>
        public static ITask AsSkippableTask(this ITask taskToSkip)
        {
            var t = taskToSkip as TaskBase;
            if (t != null)
                t.IsSkippable = true;

            return taskToSkip;
        }
    }
}
